(function(window){
  const PENDING = 'pending';
  const RESOLVED = 'resolved';
  const REJECTED = 'rejected';
  class Promise {
    constructor(executor) {
      const self = this;
      self.status = PENDING;
      self.data = undefined;
      self.callbacks = [];
      function handle(data, state, callback) {
        if(self.status !== PENDING) {
          return;
        }
        self.status = state;
        self.data = data;
        if(self.callbacks.length) {
          asyncFunction(()=>{
            self.callbacks.forEach(callbackObj=>{
              callbackObj[callback](self.data);
            })
          })
        }
      }
      function resolve(value) {
        handle(value, RESOLVED, 'onResolved')
      }
      function reject(reason) {
        handle(reason, REJECTED, 'onRejected')
      }
      try {
        executor(resolve, reject)
      } catch (error) {
        reject(error)
      }
    }
    then(onResolved, onRejected){
      onResolved = typeof onResolved === 'function'? onResolved: value=>value;
      onRejected = typeof onRejected === 'function'? onRejected: reason=>{throw reason};
      const self = this;
      return new Promise((resolve, reject)=>{
        function handle(callback){
          try {
            const result = callback(self.data);
            if(result instanceof Promise) {
              result.then(resolve, reject)
            }else{
              resolve(result)
            }
          } catch (error) {
            reject(error);
          }
        }
        if(self.status === PENDING){
          self.callbacks.push({
            onResolved() {
              handle(onResolved)
            },
            onRejected() {
              handle(onRejected)
            }
          })
        }else if(self.status === RESOLVED){
          asyncFunction(() => {
            handle(onResolved);
          });
        }else{
          asyncFunction(() => {
            handle(onRejected);
          });
        }
      })
    }
    catch(onRejected) {
      return this.then(undefined, onRejected)
    }
    static resolve(value) {
      return new Promise((resolve, reject)=>{
        if(value instanceof Promise) {
          value.then(resolve, reject)
        }else {
          resolve(value)
        }
      })
    }
    static reject(reason) {
      return new Promise((resolve, reject)=>{
        reject(reason)
      })
    }
    static all(promises) {
      const values = [];
      let count = 0;
      return new Promise((resolve, reject)=>{
        promises.forEach((p, index)=>{
          if(!(p instanceof Promise)) {
            p = Promise.resolve(p)
          }
          p.then(value=>{
            count++;
            values[index] = value;
            if(count === promises.length){
              resolve(values)
            }
          }, reject)
        })
      })
    }
    static race(promises) {
      return new Promise((resolve, reject)=>{
        promises.forEach((p, index)=>{
          if(!(p instanceof Promise)) {
            p = Promise.resolve(p)
          }
          p.then(resolve, reject)
        })
      })
    }
  }
  function asyncFunction(callback) {
    const nodeText = document.createTextNode('1');
    new MutationObserver(callback).observe(nodeText, {characterData: true});
    nodeText.data = '2';
  }
  window.Promise = Promise;
})(window)